/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pobicos;

/**
 *
 * @author marcin
 */
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import java.awt.Paint;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.util.Collection;

import org.apache.commons.collections15.Transformer;
import edu.uci.ics.jung.algorithms.layout.TreeLayout;
//import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.DelegateTree;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
import edu.uci.ics.jung.visualization.picking.PickedState;
import edu.uci.ics.jung.visualization.renderers.BasicVertexLabelRenderer;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.util.List;
import javax.swing.JPanel;
import org.apache.commons.collections15.Factory;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import java.util.ArrayList;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.StringTokenizer;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;

/**
 *
 * @author marcin
 * 
 */
@SuppressWarnings("serial")
public class JungSubtreePanel {

    /**
     * the graph
     */
    private Forest<String, Integer> graph;
    private SparseMultigraph<String, Integer> graph2;
    /**
     * 
     */
    private Integer nodeCounter_ = 0;
    /**
     * 
     */
    private Integer dimX_ = 0;
    /**
     * 
     */
    private Integer dimY_ = 0;
    /**
     * the visual component and renderer for the graph
     */
    private VisualizationViewer<String, Integer> vv;
    /**
     * 
     */
    private TreeLayout<String, Integer> layout;
    private StaticLayout<String, Integer> layout2;
    /**
     * 
     */
    private PickedState<String> picked_state;
    private GraphZoomScrollPane panel_;
    private JungTreePanel jungTreePanel_;
    private Integer root_;
    private int edgeNumberCounter;
    
    private Operator currentOperator_=null;
    
    private int currentEdge_=0;
    private int operatorsCounter=100;

    @SuppressWarnings("unchecked")
    public JungSubtreePanel(JungTreePanel jungTreePanel) {
        graph = new DelegateTree<String, Integer>();
        graph2 = new SparseMultigraph<String,Integer>();       

        jungTreePanel_ = jungTreePanel;
        root_ = jungTreePanel.currentPickedVertex;
        
    }

    public void setUpTheTree(int dimX, int dimY) {

        dimX_ = dimX;
        dimY_ = dimY;

        createTree();

        layout = new TreeLayout<String, Integer>(graph, 100);
        /**
         * Setup vertex positions
         */
        int j = 0;
        for (String s : graph.getVertices()) {
            if (graph.getParent((String) s) != null) {
                int width = 200;
                //cele roota
                if ((graph.getParent((String) s).equals(root_.toString())) && (graph.getChildCount(s) == 0)) {
                    int temp = graph.getNeighborCount(root_.toString()) - jungTreePanel_.getSubGraphCollection().size();
                    int start = 0;
                    int tempWidth = 0;
                    if (temp == 1) {
                        start = 0;
                        tempWidth = (width / temp);
                    } else if (temp == 2) {
                        start = (width / 2);
                        tempWidth = width;
                    } else {
                        start = (width / 2);
                        tempWidth = 100;
                    }
                    Point2D d = new Point2D.Double(layout.transform(root_.toString()).getX() - start + j, layout.transform(root_.toString()).getY() + 30);
                    layout.setLocation(s, d);
                    j += tempWidth;
                    //cele agnetow podrzednych
                } else if (graph.getChildCount(s) == 0) {
                    Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY() + 100);
                    layout.setLocation(s, d);
                }
                //agnety podrzedne
                if (graph.getChildCount(s) != 0) {
                    Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY() + 200);
                    layout.setLocation(s, d);
                }
                //root 
            } else {
                Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY());
                layout.setLocation(s, d);
            }
        }
        /**
         * graph's layout has to be convertet from TreeLayout to MultiGraph in order to connect Operator correctly
         */
           
        for (String n : graph.getVertices()) {
            graph2.addVertex(n);
        }
        for (Integer e : graph.getEdges()) {
            graph2.addEdge(e, graph.getSource(e), graph.getDest(e), EdgeType.DIRECTED);
        }
        
        edgeNumberCounter=graph.getEdgeCount();
      //  System.out.println("Show me edgeCounter : " + edgeNumberCounter);
        
        //Jezeli do tej pory poddrzewo operatorów nie było edytowane
        Agent tempAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(root_).getData();
        if (tempAgent.getOperatorsList() == null) {
            layout2 = new StaticLayout<String, Integer>(graph2);
            /**
             * Redraw the locations to the new graph
             */
            Comparator<String> t = new Comparator<String>() {
                public int compare(String o1, String o2) {
                    StringTokenizer st1 = new StringTokenizer(o1, ".");
                    StringTokenizer st2 = new StringTokenizer(o2, ".");                  
                    Integer c1 = Integer.valueOf(st1.nextToken());
                    Integer c2 = Integer.valueOf(st2.nextToken());
                    return c1.compareTo(c2);
                }
            };
            List<String> tempList = new ArrayList<String>();
            for(String s : graph.getVertices()) {
                tempList.add(s);
            }          
            
          // Collections.sort(tempList,t);
            
            for (String s : tempList) {
                Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY());
                layout2.setLocation(s, d);
            }
        } else {
            for (Operator ot : tempAgent.getOperatorsList()) {
                graph2.addVertex(ot.getName());
            }
            for (Operator ot : tempAgent.getOperatorsList()) {
                int counter=0;
                for(String s : ot.getInputs()) {
                    graph2.addEdge(edgeNumberCounter, ot.getName(), ot.getInputs().get(counter++),EdgeType.DIRECTED);
                    edgeNumberCounter++;
                }
                counter=0;
                for(String s : ot.getOutputs()) {
                    graph2.addEdge(edgeNumberCounter, ot.getName(), ot.getOutputs().get(counter++),EdgeType.DIRECTED);
                    edgeNumberCounter++;
                }
            }

            layout2 = new StaticLayout<String, Integer>(graph2);
            int k = 0;
            for (String s : graph2.getVertices()) {
                if (layout.getGraph().containsVertex(s)) {
                    Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY());
                    layout2.setLocation(s, d);
                } else {
                    Point2D d = new Point2D.Double(layout.transform(root_.toString()).getX()-50+k,
                            layout.transform(root_.toString()).getY()+100);
                    layout2.setLocation(s, d);
                    edgeNumberCounter+=graph2.getNeighborCount(s);
                    k+=100;
                }
            }
            
        }

//        if (jungTreePanel_.subGraphMap.get(root_) == null) {
//
//            layout2 = new StaticLayout<String, Integer>(graph2);
//            /**
//             * Redraw the locations to the new graph
//             */
//            for (String s : graph.getVertices()) {
//                Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY());
//                layout2.setLocation(s, d);
//            }
//        } else {
//            SparseMultigraph<String, Integer> tempGraph = jungTreePanel_.subGraphMap.get(root_);
//            layout2 = new StaticLayout<String, Integer>(tempGraph);
//            int k=0;
//            for (String s : tempGraph.getVertices()) {            
//                if (layout.getGraph().containsVertex(s)) {
//                    Point2D d = new Point2D.Double(layout.transform(s).getX(), layout.transform(s).getY());
//                    layout2.setLocation(s, d);
//                } else {
//                    Point2D d = new Point2D.Double(layout.transform(root_.toString()).getX()-50+k,
//                            layout.transform(root_.toString()).getY()+100);
//                    layout2.setLocation(s, d);
//                    edgeNumberCounter+=tempGraph.getNeighborCount(s);
//                    k+=60;
//                }
//            }
//        }

        vv = new VisualizationViewer<String, Integer>(layout2, new Dimension(dimX_, dimY_));
        
        picked_state = vv.getPickedVertexState();
        vv.setBackground(Color.WHITE);
        vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());

        vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);    

        //Edge label
        //     vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<Integer>() );    
        //Vertex label positioner
 //       vv.getRenderer().setVertexLabelRenderer(new MyLabelPositioner());
        // Vertex label
        vv.getRenderContext().setVertexLabelTransformer(new MyToStringLabeler());
        // Vertex color
        vv.getRenderContext().setVertexFillPaintTransformer(new MyVertexFillPaintFunction<String>());
        // Edge color
        vv.getRenderContext().setEdgeDrawPaintTransformer(new MyEdgeDrawPaintTransformer<Integer>());
        vv.getRenderContext().setArrowDrawPaintTransformer(new MyEdgeDrawPaintTransformer<Integer>());
        vv.getRenderContext().setArrowFillPaintTransformer(new MyEdgeDrawPaintTransformer<Integer>());
        // Vertex shape
        vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());

        panel_ = new GraphZoomScrollPane(vv);
        panel_.setSize(dimX_, dimY_);

        final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse() {

            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                //My code here
                //MUST BE HERE!!!!!!!!!!!!!!!!!!!!!
                //super.mousePressed(e);
            }
        };
        graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
        vv.setGraphMouse(graphMouse);
   
        JComboBox modeBox = graphMouse.getModeComboBox();
        modeBox.addItemListener(graphMouse.getModeListener());

        JButton addNodeButton = new JButton("Add Edge");
        addNodeButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
                if (picked.size() == 1) {
                    String selectedNode = (String) picked.iterator().next();
                    if (graph.getChildCount(selectedNode) == 0) {
                        if (currentOperator_ != null) {
                            graph2.addEdge(edgeNumberCounter, currentOperator_.getName(), selectedNode, EdgeType.DIRECTED);
                            edgeNumberCounter++;
                            currentEdge_++;
                            //graph2.addEdge(edgeNumberCounter, currentOperator_.getName(), selectedNode, EdgeType.DIRECTED);
                            vv.getPickedVertexState().clear();
                            //refresh layout
                            vv.setGraphLayout(layout2);
                            vv.repaint();
                            if (currentEdge_ == currentOperator_.getNumberOfInputs() + currentOperator_.getNumberOfOutputs()) {
                                //FROM SAVE STATE METHOD
                                List<String> tempOutput = new ArrayList<String>();
                                String outputVertex = null;
                                Collection<String> vertexNeighbors = graph2.getNeighbors(currentOperator_.getName());
                                for (String s : vertexNeighbors) {
                                    if (graph.getChildren(root_.toString()).contains(s)) {
                                        outputVertex = s;
                                        tempOutput.add(outputVertex);
                                    }
                                }
                                for (String s : tempOutput) {
                                    vertexNeighbors.remove((String) s);
                                }
                                currentOperator_.setOutputs(tempOutput);
                                currentOperator_.setInputs(new ArrayList(vertexNeighbors));
                                //Show dilalog from implementation, and zerox currentOperator and counter
                                if (currentOperator_.getFileImplName() == null) {
                                } else {
                                    currentOperator_.getFileImplName().showDialog(jungTreePanel_.getMainViewFrame(), currentOperator_.getInputs());
                                }
                                currentEdge_ = 0;
                                currentOperator_ = null;
                            }
                        }
                    } else {
                        vv.getPickedVertexState().clear();
                       JOptionPane.showMessageDialog(null, "Only goals can be selected!");
                    }
                }
            }
        });

        JButton deleteNodeButton = new JButton("Delete");
        deleteNodeButton.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
                if (picked.size() == 1) {
                    String nodeToDelete = (String) picked.iterator().next();
                    Agent tempAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(jungTreePanel_.currentPickedVertex).getData();
                    for (Operator o :tempAgent.getOperatorsList()) {
                        if (o.getName().equals(nodeToDelete)) {
                            currentOperator_ = o;
                        }
                    }
                    if (currentOperator_ != null) {
                        if (nodeToDelete.equals(currentOperator_.getName())) {
                            graph2.removeVertex(nodeToDelete);
                            tempAgent.getOperatorsList().remove(currentOperator_);
                            SubtreeEditor.OperatorsComboBox.removeItem(currentOperator_.getName()+":"
                                    +currentOperator_.getNumberOfInputs()+":"+currentOperator_.getNumberOfOutputs());
                            vv.getPickedVertexState().clear();
                            //refresh layout
                            vv.setGraphLayout(layout2);
                            vv.repaint();
                        }
                    }
                }
            }
        });

        /**
         *  Adding control buttons to the control panel
         */
        JPanel scaleGrid = new JPanel(new GridLayout(1, 5));

        JPanel controls = new JPanel();

        scaleGrid.add(addNodeButton);
        scaleGrid.add(deleteNodeButton);
        scaleGrid.add(modeBox);
        controls.add(scaleGrid);
        panel_.add(controls, BorderLayout.SOUTH);
 
    }
    public void vvSetSize(int x, int y) {
        panel_.setSize(x, y);
   //     vv.setSize(x, y);
        vv.repaint();
    }

    /**
     * Create the subtree
     */
    private void createTree() {
        
        Collection<Integer> tempCollection = jungTreePanel_.getSubGraphCollection();
        //adding root
        graph.addVertex(root_.toString());
        //adding children
        for (Integer x : tempCollection) {
            graph.addEdge(nodeCounter_, root_.toString(), x.toString());
            nodeCounter_++;
            //adding children's goals
            Agent tempAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(x).getData();
            for (String y : tempAgent.getType().getGoalsList()) {
                graph.addEdge(nodeCounter_, x.toString(), x.toString() + "." + y);
                nodeCounter_++;
            }
        }
        //adding root's goals
        Agent rootAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(root_).getData();
        for (String y : rootAgent.getType().getGoalsList()) {
            graph.addEdge(nodeCounter_, root_.toString(), root_.toString() + "." + y);
            //  graph.addEdge(nodeCounter_, root_.toString(), y);
            nodeCounter_++;
        }   
    }

    public PickedState<String> getPickedState() {
        return picked_state;
    }

    public GraphZoomScrollPane getTreeJPanel() {
        return panel_;
    }

    public void drawOperator(final Operator ot) {
        
        currentOperator_ = ot;

        /**
         * start matching operator with selected goals
         */
       EditingModalGraphMouse<String, Integer>  graphMouse2 = new EditingModalGraphMouse<String, Integer>(
                vv.getRenderContext(), 
                new Factory<String>() {
            public String create() {
                final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
                graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
                vv.setGraphMouse(graphMouse);
                graph2 = (SparseMultigraph) layout2.getGraph();
                vv.setGraphLayout(layout2);
                vv.repaint();
               // ot.setName(ot.getName()+operatorsCounter++);
                return ot.getName();
            }
        }, new Factory<Integer>() {

            public Integer create() {
                return 0;
            }
        });
        graphMouse2.setMode(ModalGraphMouse.Mode.EDITING);
        vv.setGraphMouse(graphMouse2);
        nodeCounter_++;
        
    }

    /**
     * 
     * @param opetatorsList
     * @return  List of operators added to subtree
     */
    public  List<Operator> saveState(List<Operator> opetatorsList) {
        for(Operator operator : opetatorsList) {
            List<String> tempOutput = new ArrayList<String>();
            String outputVertex = null;
            Collection<String> vertexNeighbors = graph2.getNeighbors(operator.getName());
            for(String s : vertexNeighbors) {
                if(graph.getChildren(root_.toString()).contains(s)) {
                    outputVertex = s;
                   tempOutput.add(outputVertex);
                }
            }
            for(String s : tempOutput) {
                vertexNeighbors.remove((String)s);
            }
           operator.setOutputs(tempOutput);
           operator.setInputs(new ArrayList(vertexNeighbors));

           System.out.println("Operator name: " + operator.getName() );
           for(String s : operator.getInputs() ) {
               System.out.println("Input : " + s);
            }
            for (String s : operator.getOutputs()) {
                System.out.println("Outputs : " + s);
            }
        }
         Agent tempAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(root_).getData();
         tempAgent.setOperatorsList(opetatorsList);
        return opetatorsList;
    }

    private class MyLabelPositioner<V, E> extends BasicVertexLabelRenderer<V, E> {

        
        @Override
        public Position getPosition() {
            return Renderer.VertexLabel.Position.S;
            //return super.getPosition();
        }
    }

    private class MyVertexFillPaintFunction<V> implements Transformer<V, Paint> {

        public Paint transform(V v) {
            if (picked_state.isPicked((String) v)) {
                return Color.YELLOW;
            } else if (!graph.containsVertex((String) v)) {
                Color myColor2 = new Color(214, 227, 188);
                return myColor2;              
            } else if (graph.getChildCount((String) v) == 0) {           
                return Color.orange;
            } else if (graph.getParent((String) v) == null) {
                return Color.RED;
            } else {
                return vv.getBackground();
            }
        }
    }

    private class MyEdgeDrawPaintTransformer<E> implements Transformer<E, Paint> {
        
        public Paint transform(E e) {
            if(graph.getChildEdges(root_.toString()).contains((Integer)e)) {
                return vv.getBackground();
            } else if ((Integer) e >= graph.getEdgeCount()) {
                Color myColor2 = new Color(214, 227, 188);
                return myColor2;   
            } else {
                return Color.lightGray;
            }
        }
    }
    private class MyToStringLabeler<V> implements Transformer<V,String> {

        public String transform(V i) {
            if (graph.getChildCount((String) i) != 0 ) {
                Agent tempAgent = (Agent) jungTreePanel_.getLogicalTree().getNodeAt(Integer.parseInt((String) i)).getData();
                return (String) i + ": "+tempAgent.getType().getAgentTypeName();
            }
            return (String) " \n \n"+i+" \n \n \n";
        }
    }

    private class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {

        ClusterVertexShapeFunction() {
            setSizeTransformer(new Transformer<V, Integer>() {

                public Integer transform(V v) {
                     if (!graph.containsVertex((String )v)){
                        return 30;
                     } else if (graph.getChildCount((String) v) == 0) {
                        return 10;
                    } else if (graph.containsVertex((String )v)) {
                        return 20;
                    } else {
                        return 20;
                    }
                }
            });
        }
        @Override
        public Shape transform(V v) {
            if (graph.getChildCount((String) v) == 0) {
                return factory.getRegularPolygon(v, 4);
            }
            return super.transform(v);
        }
    }
}
